/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libzlang_stdtypes.h"

#include "list.h"

struct QueueNode
{
	struct ZlangObject		*member ;
	struct list_head		node ;
} ;

struct ZlangDirectProperty_queue
{
	struct list_head		queue ;
	
	int32_t				queue_length ;
} ;

ZlangInvokeFunction ZlangInvokeFunction_queue_Produce_object;
int ZlangInvokeFunction_queue_Produce_object( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1_obj = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*ext_obj_name = NULL ;
	char					*clone_obj_name = NULL ;
	struct QueueNode			*queue_node = NULL ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	ext_obj_name = GetObjectEmbellishName( obj ) ;
	clone_obj_name = GetCloneObjectName( in1_obj ) ;
	if( ext_obj_name )
	{
		if( clone_obj_name == NULL || STRCMP( ext_obj_name , != , clone_obj_name ) )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "object type not matched" )
			CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
			IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
			return 0;
		}
	}
	
	queue_node = (struct QueueNode *)ZLMALLOC( sizeof(struct QueueNode) ) ;
	if( queue_node == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for queue node" )
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ZLANG_ERROR_ALLOC;
	}
	memset( queue_node , 0x00 , sizeof(struct QueueNode) );
	
	queue_node->member = AllocObject( rt ) ;
	if( queue_node->member == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for object" )
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ZLANG_ERROR_ALLOC;
	}
	
	nret = ReferObject( rt , queue_node->member , in1_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject failed[%d]" , nret )
		DestroyObject( rt , queue_node->member );
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return nret;
	}
	
	nret = SetObjectName( rt , queue_node->member , GetObjectName(in1_obj) ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SetObjectName failed[%d]" , nret )
		DestroyObject( rt , queue_node->member );
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return nret;
	}
	
	LIST_ADD( & (queue_node->node) , & (queue_direct_prop->queue) );
	
	queue_direct_prop->queue_length++;
	
	CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	return 0;
}

int queue_Peek( struct ZlangRuntime *rt , struct ZlangDirectProperty_queue *queue_direct_prop , struct QueueNode **pp_queue_node , struct ZlangObject *out1_obj )
{
	struct QueueNode	*queue_node = NULL ;
	struct ZlangObject	*member = NULL ;
	int			nret = 0 ;
	
	queue_node = list_last_entry_or_null( & (queue_direct_prop->queue) , struct QueueNode , node ) ;
	if( queue_node == NULL )
	{
		UnreferObject( rt , out1_obj );
		if( pp_queue_node )
			(*pp_queue_node) = NULL ;
		return 0;
	}
	
	member = queue_node->member ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "member[%p]" , member )
	nret = ReferObject( rt , out1_obj , member ) ;
	if( nret )
	{
		UnreferObject( rt , out1_obj );
		return nret;
	}
	
	if( pp_queue_node )
		(*pp_queue_node) = queue_node ;
	return 0;
}
ZlangInvokeFunction ZlangInvokeFunction_queue_Consume;
int ZlangInvokeFunction_queue_Consume( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	struct QueueNode			*queue_node = NULL ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	nret = queue_Peek( rt , queue_direct_prop , & queue_node , out1_obj ) ;
	if( nret )
	{
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return nret;
	}
	
	if( queue_node )
	{
		list_del( & (queue_node->node) );
		DestroyObject( rt , queue_node->member );
		ZLFREE( queue_node );
		
		queue_direct_prop->queue_length--;
	}
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_queue_Peek;
int ZlangInvokeFunction_queue_Peek( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	nret = queue_Peek( rt , queue_direct_prop , NULL , out1_obj ) ;
	if( nret )
	{
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return nret;
	}
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

static void queue_RemoveAll( struct ZlangRuntime *rt , struct ZlangDirectProperty_queue *queue_direct_prop )
{
	struct QueueNode	*curr_node = NULL ;
	struct QueueNode	*prev_node = NULL ;
	
	list_for_each_entry_safe_reverse( curr_node , prev_node , & (queue_direct_prop->queue) , struct QueueNode , node )
	{
		list_del( & (curr_node->node) );
		DestroyObject( rt , curr_node->member );
		ZLFREE( curr_node );
	}
	
	return;
}

ZlangInvokeFunction ZlangInvokeFunction_queue_RemoveAll;
int ZlangInvokeFunction_queue_RemoveAll( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	queue_RemoveAll( rt , queue_direct_prop );
	queue_direct_prop->queue_length = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_queue_Length;
int ZlangInvokeFunction_queue_Length( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	CallRuntimeFunction_int_SetIntValue( rt , out1_obj , queue_direct_prop->queue_length );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_queue_IsEmpty;
int ZlangInvokeFunction_queue_IsEmpty( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	if( queue_direct_prop->queue_length == 0 )
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	else
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_queue_IsNotEmpty;
int ZlangInvokeFunction_queue_IsNotEmpty( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	if( queue_direct_prop->queue_length != 0 )
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	else
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangCreateDirectPropertyFunction ZlangCreateDirectProperty_queue;
void *ZlangCreateDirectProperty_queue( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = NULL ;
	
	queue_direct_prop = (struct ZlangDirectProperty_queue *)ZLMALLOC( sizeof(struct ZlangDirectProperty_queue) ) ;
	if( queue_direct_prop == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for entity" )
		return NULL;
	}
	memset( queue_direct_prop , 0x00 , sizeof(struct ZlangDirectProperty_queue) );
	
	INIT_LIST_HEAD( & (queue_direct_prop->queue) );
	
	return (struct ZlangEntity *)queue_direct_prop;
}

ZlangDestroyDirectPropertyFunction ZlangDestroyDirectProperty_queue;
void ZlangDestroyDirectProperty_queue( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	
	queue_RemoveAll( rt , queue_direct_prop );
	
	ZLFREE( queue_direct_prop );
	
	return;
}

ZlangSummarizeDirectPropertySizeFunction ZlangSummarizeDirectPropertySize_queue;
void ZlangSummarizeDirectPropertySize_queue( struct ZlangRuntime *rt , struct ZlangObject *obj , size_t *summarized_obj_size , size_t *summarized_direct_prop_size )
{
	struct ZlangDirectProperty_queue	*queue_direct_prop = GetObjectDirectProperty(obj) ;
	struct QueueNode			*queue_node = NULL ;
	
	list_for_each_entry( queue_node , & (queue_direct_prop->queue) , struct QueueNode , node )
	{
		SummarizeObjectSize( rt , queue_node->member , summarized_obj_size , summarized_direct_prop_size );
	}
	
	SUMMARIZE_SIZE( summarized_direct_prop_size , sizeof(struct ZlangDirectProperty_queue) )
	
	return;
}

static struct ZlangDirectFunctions direct_funcs_queue =
	{
		ZLANG_OBJECT_queue ,
		
		ZlangCreateDirectProperty_queue ,
		ZlangDestroyDirectProperty_queue ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		ZlangSummarizeDirectPropertySize_queue ,
	} ;

ZlangImportObjectFunction ZlangImportObject_queue;
struct ZlangObject *ZlangImportObject_queue( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	struct ZlangFunction	*func = NULL ;
	int			nret = 0 ;
	
	nret = ImportObject( rt , & obj , ZLANG_OBJECT_queue , & direct_funcs_queue , sizeof(struct ZlangDirectFunctions) , NULL ) ;
	if( nret )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_LINK_FUNC_TO_ENTITY , "import object to global objects heap" )
		return NULL;
	}
	
	/* queue.Produce(object) */
	func = AddFunctionAndParametersInObject( rt , obj , "Produce" , "Produce(object)" , ZlangInvokeFunction_queue_Produce_object , ZLANG_OBJECT_bool , ZLANG_OBJECT_object,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.Produce(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "Produce" , "Produce(string)" , ZlangInvokeFunction_queue_Produce_object , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.Produce(int) */
	func = AddFunctionAndParametersInObject( rt , obj , "Produce" , "Produce(int)" , ZlangInvokeFunction_queue_Produce_object , ZLANG_OBJECT_bool , ZLANG_OBJECT_int,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.Peek() */
	func = AddFunctionAndParametersInObject( rt , obj , "Peek" , "Peek()" , ZlangInvokeFunction_queue_Peek , ZLANG_OBJECT_object , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.Consume() */
	func = AddFunctionAndParametersInObject( rt , obj , "Consume" , "Consume()" , ZlangInvokeFunction_queue_Consume , ZLANG_OBJECT_object , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.RemoveAll() */
	func = AddFunctionAndParametersInObject( rt , obj , "RemoveAll" , "RemoveAll()" , ZlangInvokeFunction_queue_RemoveAll , ZLANG_OBJECT_void , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.Length() */
	func = AddFunctionAndParametersInObject( rt , obj , "Length" , "Length()" , ZlangInvokeFunction_queue_Length , ZLANG_OBJECT_int , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.IsEmpty() */
	func = AddFunctionAndParametersInObject( rt , obj , "IsEmpty" , "IsEmpty()" , ZlangInvokeFunction_queue_IsEmpty , ZLANG_OBJECT_bool , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* queue.IsNotEmpty() */
	func = AddFunctionAndParametersInObject( rt , obj , "IsNotEmpty" , "IsNotEmpty()" , ZlangInvokeFunction_queue_IsNotEmpty , ZLANG_OBJECT_bool , NULL ) ;
	if( func == NULL )
		return NULL;
	
	return obj ;
}

